
#include <cassert>
#include <cstdlib>

#include <DXGI.h>
#include <D3D10_1.h>
#include <D3DX11.h>
#include <D3DX10math.h>
#include <D3DX11async.h>

#include "Shaders\RT2ScreenShader.h"

#pragma comment(lib, "D3D11.lib")
#pragma comment(lib, "D3DX11.lib")


ID3D10Blob* RT2ScreenShader::s_vshader_buffer = NULL;
ID3D10Blob* RT2ScreenShader::s_pshader_buffer = NULL;


BOOL RT2ScreenShader::s_loaded = FALSE;


RT2ScreenShader::RT2ScreenShader()
	: m_vshader(NULL),
	  m_pshader(NULL),
      m_binded(FALSE),
	  m_source(NULL)
{
}


RT2ScreenShader::~RT2ScreenShader()
{
	// Unbind if needed.
	if(m_binded)
		Unbind();
}


BOOL RT2ScreenShader::IsLoaded()
{
	return s_loaded;
}


BOOL RT2ScreenShader::Load()
{
	assert(!s_vshader_buffer);
	assert(!s_pshader_buffer);
	assert(!s_loaded);

	ID3D10Blob* error_buffer = NULL;
	LPCSTR compile_error = NULL;
	HRESULT result;

	// Load and compile pixel shader.
	result = D3DX11CompileFromFile(TEXT("Shaders\\RT2Screen.vs"),
								NULL, NULL, "Main", "vs_5_0",
								D3D10_SHADER_ENABLE_STRICTNESS, 0, NULL,
								&s_vshader_buffer, &error_buffer, NULL);

	if(FAILED(result))
		compile_error = static_cast< LPCSTR >(error_buffer->GetBufferPointer());

	assert(SUCCEEDED(result));

	// Load and compile pixel shader.
	result = D3DX11CompileFromFile(TEXT("Shaders\\RT2Screen.ps"),
								NULL, NULL, "Main", "ps_5_0",
								D3D10_SHADER_ENABLE_STRICTNESS, 0, NULL,
								&s_pshader_buffer, &error_buffer, NULL);
	
	if(FAILED(result))
		compile_error = static_cast< LPCSTR >(error_buffer->GetBufferPointer());

	assert(SUCCEEDED(result));

	// Change state to loaded.
	s_loaded = TRUE;

	return TRUE;
}


VOID RT2ScreenShader::Unload()
{
	assert(s_vshader_buffer);
	assert(s_pshader_buffer);
	assert(s_loaded);

	s_vshader_buffer->Release();
	s_vshader_buffer = NULL;

	s_pshader_buffer->Release();
	s_pshader_buffer = NULL;

	// Change state to unloaded.
	s_loaded = FALSE;
	s_pshader_buffer = NULL;
}


BOOL RT2ScreenShader::IsBinded() CONST
{
	return m_binded;
}


BOOL RT2ScreenShader::Bind()
{
	assert(s_vshader_buffer);
	assert(s_pshader_buffer);
	assert(s_loaded);
	assert(!m_vshader);
	assert(!m_pshader);
	assert(!m_binded);

	HRESULT result;

	// Create the vertex shader buffer.
	result = GetDevice()->CreateVertexShader(s_vshader_buffer->GetBufferPointer(),
											 s_vshader_buffer->GetBufferSize(),
											 NULL,
											 &m_vshader);

	assert(SUCCEEDED(result));

	// Create the pixel shader buffer.
	result = GetDevice()->CreatePixelShader(s_pshader_buffer->GetBufferPointer(),
											s_pshader_buffer->GetBufferSize(),
											NULL,
											&m_pshader);

	assert(SUCCEEDED(result));

	// Setup the input layout description.
	const UINT input_count = 2;
	D3D11_INPUT_ELEMENT_DESC input_desc[input_count];

	input_desc[0].SemanticName = "POSITION";
	input_desc[0].SemanticIndex = 0;
	input_desc[0].Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
	input_desc[0].InputSlot = 0;
	input_desc[0].AlignedByteOffset = 0;
	input_desc[0].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
	input_desc[0].InstanceDataStepRate = 0;

	input_desc[1].SemanticName = "TEXCOORD";
	input_desc[1].SemanticIndex = 0;
	input_desc[1].Format = DXGI_FORMAT_R32G32_FLOAT;
	input_desc[1].InputSlot = 0;
	input_desc[1].AlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT;
	input_desc[1].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
	input_desc[1].InstanceDataStepRate = 0;

	// Create the input layout.
	result = GetDevice()->CreateInputLayout(input_desc,
										    input_count,
									        s_vshader_buffer->GetBufferPointer(),
										    s_vshader_buffer->GetBufferSize(),
									        &m_layout);

	assert(SUCCEEDED(result));

	// Change state to binded.
	m_binded = TRUE;

	return m_binded;
}


VOID RT2ScreenShader::Unbind()
{
	assert(m_vshader);
	assert(m_pshader);
	assert(m_binded);

	m_vshader->Release();
	m_vshader = NULL;

	m_pshader->Release();
	m_pshader = NULL;
	
	m_source = NULL;

	// Change state to unbinded.
	m_binded = FALSE;
}


VOID RT2ScreenShader::Enable()
{
	assert(m_vshader);
	assert(m_pshader);
	assert(m_binded);

	// Enabling.
	GetContext()->IASetInputLayout(m_layout);
	GetContext()->VSSetShader(m_vshader, NULL, 0);
	GetContext()->PSSetShader(m_pshader, NULL, 0);
}


BOOL RT2ScreenShader::SetSource(CONST RenderTarget* source)
{
	assert(m_vshader);
	assert(m_pshader);
	assert(m_binded);

	m_source = source;

	// Unset the source.
	if(!m_source)
	{
		GetContext()->PSSetShaderResources(0, 1, NULL);
		return TRUE;
	}

	ID3D11ShaderResourceView* resource = m_source->GetResource();
	GetContext()->PSSetShaderResources(0, 1, &resource);

	return TRUE;
}